//
// Created by martin on 2/16/22.
//

#include "TcpServer.h"
#include "muduo/base/Logging.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/EventLoopThreadPool.h"
#include "muduo/net/InetAddress.h"
#include "muduo/net/SocketsOps.h"
#include "muduo/net/Callbacks.h"
#include "muduo/net/Acceptor.h"
#include "muduo/net/TcpConnection.h"

using namespace muduo;
using namespace muduo::net;

TcpServer::TcpServer(EventLoop* loop, // TcpServer所属baseLoop_, 通常是main线程持有的EventLoop
	const InetAddress& listenAddr,
	const std::string& nameArg,
	Option option)
	: loop_(CHECK_NOTNULL(loop)),
	ipPort_(listenAddr.toIpPort()),
	name_(nameArg),
	acceptor_(new Acceptor(loop, listenAddr, option == kReusePort)),
	threadPool_(new EventLoopThreadPool(loop, name_)),
	connectionCallback_(defaultConnectionCallback),
	messageCallback_(defaultMessageCallback),
	nextConnId_(1)
{
	acceptor_->setNewConnectionCallback(
		std::bind(&TcpServer::newConnection, this, _1, _2));
}

/**
 * 新建一个TcpConnection对象, 用于连接管理.
 * @details 新建的TcpConnection对象会加入内部ConnectionMap.
 * @param sockfd accept返回的连接fd (accepted socket fd)
 * @param peerAddr 对端ip地址信息
 * @note 必须在所属loop线程运行
 */
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
	loop_->assertInLoopThread();
	/* 从EventLoop线程池中，取出一个EventLoop对象构造TcpConnection对象，便于均衡各EventLoop负责的连接数　*/
	EventLoop* ioLoop = threadPool_->getNextLoop(); // next event loop from the event loop thread pool

	/* 设置连接对象名称, 包含基础名称+ip地址+端口号+连接Id
	 * 因为要作为ConnectionMap的key, 要确保运行时唯一性 */
	char buf[32];
	snprintf(buf, sizeof(buf), "-%s#%d", ipPort_.c_str(), nextConnId_);
	++nextConnId_;
	std::string connName = name_ + buf;

	LOG_INFO << "TcpServer::newConnection [" << name_
		<< "] - new connection [" << connName
		<< "] from " << peerAddr.toIpPort();

	InetAddress localAddr(sockets::getLocalAddr(sockfd)); // 本地ip地址信息
	// FIXME poll with zero timeout to double confirm the new connection
	// FIXME use make_shared if necessary
	/* 新建TcpConnection对象,　并加入ConnectionMap */
	TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd, localAddr, peerAddr));
	connections_[connName] = conn;
	/* 为新建TcpConnection对象设置各种回调 */
	conn->setConnectionCallback(connectionCallback_); // 连接回调
	conn->setMessageCallback(messageCallback_);       // 消息回调
	conn->setWriteCompleteCallback(writeCompleteCallback_); // 写完成回调
	conn->setCloseCallback( // 关闭连接回调
		std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe

	conn->setTcpNoDelay(true); // 禁用 Nagle 算法
	//conn->setTcpNoDelay(false); // 使能 Nagle 算法
	/* 确认连接是否已建立, 并初始化连接建立后的状态 */
	ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
}

void TcpServer::setThreadNum(int numThreads)
{
	assert(0 <= numThreads);
	threadPool_->setThreadNum(numThreads); // 实际上设置的线程池的线程数
}

/**
 * 启动TcpServer, 初始化线程池, 连接接受器Accept开始监听(Tcp连接请求)
 */
void TcpServer::start()
{
	if (started_.getAndSet(1) == 0) // 防止多次重复启动
	{
		threadPool_->start(threadInitCallback_); // 启动线程池, 并设置线程初始化完成的回调函数

		assert(!acceptor_->listening());
		loop_->runInLoop(
			std::bind(&Acceptor::listen, get_pointer(acceptor_)));
	}
}

/**
 * 转交给removeConnectionInLoop, 在所属loop线程中执行
 */
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
	// FIXME: unsafe
	loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}

/**
 * 在所属loop线程循环中, 排队移除指定tcp连接 conn
 * @param conn 指向待移除tcp连接对应TcpConnection对象
 */
void TcpServer::removeConnectionInLoop(const TcpConnectionPtr& conn)
{
	loop_->assertInLoopThread();
	LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
		<< "] - connection " << conn->name();
	// 从ConnectionMap中擦除待移除TcpConnection对象, 引用计数-1, 如果计数０, 就会释放对象.
	// 此时, conn对象还存在, 引用计数不会立即为０. 因此TcpConnection对象不会立即释放
	size_t n = connections_.erase(conn->name());
	(void)n;
	assert(n == 1);
	EventLoop* ioLoop = conn->getLoop();
	ioLoop->queueInLoop(
		std::bind(&TcpConnection::connectDestroyed, conn)); // 在所属loop线程中排队销毁TcpConnection对象
}

/**
 * 析构TcpServer对象, 销毁ConnectionMap中所有连接
 */
TcpServer::~TcpServer()
{
	loop_->assertInLoopThread();
	LOG_TRACE << "TcpServer::~TcpServer [" << name_ << "] destructing";

	// resetRequest all connection of @c connections_
	for (auto& item : connections_)
	{
		TcpConnectionPtr conn(item.second); // shared_ptr manage TcpConnection
		item.second.reset();
		conn->getLoop()->runInLoop(
			std::bind(&TcpConnection::connectDestroyed, conn));
	}
}
